Maksimalkan potensi Pandas dengan menguasai fungsi kustom. Panduan definitif ini merinci perbedaan, performa, dan kasus penggunaan terbaik untuk apply(), map(), dan applymap() dalam analisis data profesional.
Menguasai Pandas: Mendalami Fungsi Kustom dengan apply(), map(), dan applymap()
Dalam dunia sains dan analisis data, pustaka Pandas Python adalah alat yang sangat diperlukan. Pustaka ini menyediakan struktur data yang kuat, fleksibel, dan efisien yang dirancang untuk membuat pekerjaan dengan data terstruktur menjadi mudah dan intuitif. Meskipun Pandas dilengkapi dengan serangkaian fungsi bawaan yang kaya untuk agregasi, pemfilteran, dan transformasi, akan tiba saatnya dalam perjalanan setiap profesional data di mana semua itu tidak cukup. Anda perlu menerapkan logika kustom Anda sendiri, aturan bisnis yang unik, atau transformasi kompleks yang tidak tersedia secara langsung.
Di sinilah kemampuan untuk menerapkan fungsi kustom menjadi kekuatan super. Namun, Pandas menawarkan beberapa cara untuk mencapainya, terutama melalui metode apply(), map(), dan applymap(). Bagi pemula, fungsi-fungsi ini bisa tampak membingungkan karena kemiripannya. Mana yang harus Anda gunakan? Kapan? Dan apa implikasi performa dari pilihan Anda?
Panduan komprehensif ini akan mengurai misteri metode-metode canggih ini. Kita akan menjelajahi masing-masing secara detail, memahami kasus penggunaan spesifiknya, dan, yang terpenting, belajar cara memilih alat yang tepat untuk pekerjaan tersebut untuk menulis kode Pandas yang bersih, efisien, dan mudah dibaca. Kita akan membahas:
- Metode
map(): Ideal untuk transformasi berbasis elemen pada satu Series. - Metode
apply(): Si serbaguna untuk operasi berbasis baris atau kolom pada DataFrame. - Metode
applymap(): Spesialis untuk operasi berbasis elemen di seluruh DataFrame. - Pertimbangan Performa: Perbedaan krusial antara metode-metode ini dan vektorisasi sejati.
- Praktik Terbaik: Kerangka pengambilan keputusan untuk membantu Anda memilih metode yang paling efisien setiap saat.
Mempersiapkan Panggung: Kumpulan Data Sampel Kita
Untuk membuat contoh kita praktis dan jelas, mari kita bekerja dengan kumpulan data yang konsisten dan relevan secara global. Kita akan membuat DataFrame sampel yang merepresentasikan data penjualan online dari perusahaan e-commerce internasional fiktif.
import pandas as pd
import numpy as np
data = {
'OrderID': [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008],
'Product': ['Laptop', 'Mouse', 'Keyboard', 'Monitor', 'Webcam', 'Headphones', 'Docking Station', 'Mouse'],
'Category': ['Electronics', 'Accessories', 'Accessories', 'Electronics', 'Accessories', 'Audio', 'Electronics', 'Accessories'],
'Price_USD': [1200, 25, 75, 300, 50, 150, 250, 30],
'Quantity': [1, 2, 1, 2, 1, 1, 1, 3],
'Country': ['USA', 'Canada', 'USA', 'Germany', 'Japan', 'Canada', 'Germany', np.nan]
}
df = pd.DataFrame(data)
print(df)
DataFrame ini memberi kita campuran tipe data yang bagus (numerik, string, dan bahkan nilai yang hilang) untuk mendemonstrasikan kemampuan penuh dari fungsi-fungsi target kita.
Metode `map()`: Transformasi Berbasis Elemen untuk Series
Apa itu `map()`?
Metode map() adalah alat khusus Anda untuk memodifikasi nilai dalam satu kolom (sebuah Pandas Series). Metode ini beroperasi berdasarkan elemen-demi-elemen. Anggap saja seperti mengatakan, "Untuk setiap item di kolom ini, cari di dalam kamus atau lewati melalui fungsi ini dan ganti dengan hasilnya."
Ini terutama digunakan untuk dua tugas:
- Mengganti nilai berdasarkan kamus (sebuah pemetaan).
- Menerapkan fungsi sederhana ke setiap elemen.
Kasus Penggunaan 1: Memetakan Nilai dengan Kamus
Ini adalah penggunaan map() yang paling umum dan efisien. Bayangkan kita ingin membuat kolom 'Department' yang lebih luas berdasarkan kolom 'Category' kita. Kita dapat mendefinisikan pemetaan dalam kamus Python dan menggunakan map() untuk menerapkannya.
category_to_department = {
'Electronics': 'Technology',
'Accessories': 'Peripherals',
'Audio': 'Technology'
}
df['Department'] = df['Category'].map(category_to_department)
print(df[['Category', 'Department']])
Keluaran:
Category Department
0 Electronics Technology
1 Accessories Peripherals
2 Accessories Peripherals
3 Electronics Technology
4 Accessories Peripherals
5 Audio Technology
6 Electronics Technology
7 Accessories Peripherals
Perhatikan betapa elegannya cara kerjanya. Setiap nilai dalam Series 'Category' dicari dalam kamus `category_to_department`, dan nilai yang sesuai digunakan untuk mengisi kolom 'Department' yang baru. Jika sebuah kunci tidak ditemukan dalam kamus, map() akan menghasilkan nilai NaN (Not a Number), yang seringkali merupakan perilaku yang diinginkan untuk kategori yang tidak terpetakan.
Kasus Penggunaan 2: Menerapkan Fungsi dengan `map()`
Anda juga dapat memberikan fungsi (termasuk fungsi lambda) ke map(). Fungsi tersebut akan dieksekusi untuk setiap elemen dalam Series. Mari kita buat kolom baru yang memberi kita label deskriptif untuk harga.
def price_label(price):
if price > 200:
return 'High-Value'
elif price > 50:
return 'Mid-Value'
else:
return 'Low-Value'
df['Price_Label'] = df['Price_USD'].map(price_label)
# Menggunakan fungsi lambda untuk tugas yang lebih sederhana:
# df['Product_Length'] = df['Product'].map(lambda x: len(x))
print(df[['Product', 'Price_USD', 'Price_Label']])
Keluaran:
Product Price_USD Price_Label
0 Laptop 1200 High-Value
1 Mouse 25 Low-Value
2 Keyboard 75 Mid-Value
3 Monitor 300 High-Value
4 Webcam 50 Low-Value
5 Headphones 150 Mid-Value
6 Docking Station 250 High-Value
7 Mouse 30 Low-Value
Kapan Menggunakan `map()`: Ringkasan Singkat
- Anda bekerja pada satu kolom (sebuah Series).
- Anda perlu mengganti nilai berdasarkan kamus atau Series lain. Ini adalah kekuatan utamanya.
- Anda perlu menerapkan fungsi berbasis elemen sederhana ke satu kolom.
Metode `apply()`: Si Serbaguna yang Andal
Apa itu `apply()`?
Jika map() adalah seorang spesialis, apply() adalah pembangkit tenaga serbaguna. Ia lebih fleksibel karena dapat beroperasi pada Series dan DataFrame. Kunci untuk memahami apply() adalah parameter axis, yang mengarahkan operasinya:
- Pada Series: Ia bekerja berbasis elemen, mirip seperti
map(). - Pada DataFrame dengan
axis=0(default): Ia menerapkan fungsi ke setiap kolom. Fungsi tersebut menerima setiap kolom sebagai sebuah Series. - Pada DataFrame dengan
axis=1: Ia menerapkan fungsi ke setiap baris. Fungsi tersebut menerima setiap baris sebagai sebuah Series.
`apply()` pada Series
Ketika digunakan pada Series, apply() berperilaku sangat mirip dengan map(). Ia menerapkan fungsi ke setiap elemen. Sebagai contoh, kita bisa mereplikasi contoh label harga kita.
df['Price_Label_apply'] = df['Price_USD'].apply(price_label)
print(df['Price_Label_apply'].equals(df['Price_Label'])) # Output: True
Meskipun keduanya tampak dapat dipertukarkan di sini, map() seringkali sedikit lebih cepat untuk substitusi kamus sederhana dan operasi berbasis elemen pada Series karena memiliki jalur yang lebih teroptimalkan untuk tugas-tugas spesifik tersebut.
`apply()` pada DataFrame (Berbasis Kolom, `axis=0`)
Ini adalah mode default untuk DataFrame. Fungsi yang Anda berikan dipanggil sekali untuk setiap kolom. Ini berguna untuk agregasi atau transformasi berbasis kolom.
Mari kita cari perbedaan antara nilai maksimum dan minimum (rentang) untuk setiap kolom numerik kita.
numeric_cols = df[['Price_USD', 'Quantity']]
def get_range(column_series):
return column_series.max() - column_series.min()
column_ranges = numeric_cols.apply(get_range, axis=0)
print(column_ranges)
Keluaran:
Price_USD 1175.0
Quantity 2.0
dtype: float64
Di sini, fungsi get_range pertama kali menerima Series 'Price_USD', menghitung rentangnya, kemudian menerima Series 'Quantity' dan melakukan hal yang sama, mengembalikan Series baru dengan hasilnya.
`apply()` pada DataFrame (Berbasis Baris, `axis=1`)
Ini bisa dibilang kasus penggunaan yang paling kuat dan umum untuk apply(). Ketika Anda perlu menghitung nilai baru berdasarkan beberapa kolom dalam baris yang sama, apply() dengan axis=1 adalah solusi andalan Anda.
Fungsi yang Anda berikan akan menerima setiap baris sebagai sebuah Series, di mana indeksnya adalah nama kolom. Mari kita hitung total biaya untuk setiap pesanan.
def calculate_total_cost(row):
# 'row' adalah Series yang merepresentasikan satu baris
price = row['Price_USD']
quantity = row['Quantity']
return price * quantity
df['Total_Cost'] = df.apply(calculate_total_cost, axis=1)
print(df[['Product', 'Price_USD', 'Quantity', 'Total_Cost']])
Keluaran:
Product Price_USD Quantity Total_Cost
0 Laptop 1200 1 1200
1 Mouse 25 2 50
2 Keyboard 75 1 75
3 Monitor 300 2 600
4 Webcam 50 1 50
5 Headphones 150 1 150
6 Docking Station 250 1 250
7 Mouse 30 3 90
Ini adalah sesuatu yang tidak bisa dilakukan oleh map(), karena terbatas pada satu kolom. Mari kita lihat contoh yang lebih kompleks. Kita ingin mengkategorikan prioritas pengiriman setiap pesanan berdasarkan kategori dan negaranya.
def assign_shipping_priority(row):
if row['Category'] == 'Electronics' and row['Country'] == 'USA':
return 'High Priority'
elif row['Total_Cost'] > 500:
return 'High Priority'
elif row['Country'] == 'Japan':
return 'Medium Priority'
else:
return 'Standard'
df['Shipping_Priority'] = df.apply(assign_shipping_priority, axis=1)
print(df[['Category', 'Country', 'Total_Cost', 'Shipping_Priority']])
Kapan Menggunakan `apply()`: Ringkasan Singkat
- Ketika logika Anda bergantung pada beberapa kolom dalam satu baris (gunakan
axis=1). Ini adalah fitur andalannya. - Ketika Anda perlu menerapkan fungsi agregasi ke bawah kolom atau melintasi baris.
- Sebagai alat aplikasi fungsi serbaguna ketika
map()tidak cocok.
Penyebutan Khusus: Metode `applymap()`
Apa itu `applymap()`?
Metode applymap() adalah spesialis lain, tetapi domainnya adalah seluruh DataFrame. Ia menerapkan sebuah fungsi ke setiap elemen tunggal dari sebuah DataFrame. Ia tidak bekerja pada Series—ini adalah metode khusus DataFrame.
Anggap saja seperti menjalankan map() pada setiap kolom secara bersamaan. Ini berguna untuk transformasi yang luas dan menyeluruh, seperti pemformatan atau konversi tipe, di semua sel.
DataFrame.applymap() sedang dalam proses depresiasi. Cara baru yang direkomendasikan adalah menggunakan DataFrame.map(). Fungsionalitasnya sama. Kami akan menggunakan applymap() di sini untuk kompatibilitas, tetapi sadarilah perubahan ini untuk kode di masa depan.
Contoh Praktis
Katakanlah kita memiliki sub-DataFrame hanya dengan kolom numerik kita dan kita ingin memformat semuanya sebagai string mata uang untuk sebuah laporan.
numeric_df = df[['Price_USD', 'Quantity', 'Total_Cost']]
# Menggunakan fungsi lambda untuk memformat setiap angka
formatted_df = numeric_df.applymap(lambda x: f'${x:,.2f}')
print(formatted_df)
Keluaran:
Price_USD Quantity Total_Cost
0 $1,200.00 $1.00 $1,200.00
1 $25.00 $2.00 $50.00
2 $75.00 $1.00 $75.00
3 $300.00 $2.00 $600.00
4 $50.00 $1.00 $50.00
5 $150.00 $1.00 $150.00
6 $250.00 $1.00 $250.00
7 $30.00 $3.00 $90.00
Penggunaan umum lainnya adalah untuk membersihkan DataFrame data string dengan, misalnya, mengubah semuanya menjadi huruf kecil.
string_df = df[['Product', 'Category', 'Country']].copy() # Buat salinan untuk menghindari SettingWithCopyWarning
# Pastikan semua nilai adalah string untuk mencegah kesalahan
string_df = string_df.astype(str)
lower_df = string_df.applymap(str.lower)
print(lower_df)
Kapan Menggunakan `applymap()`: Ringkasan Singkat
- Ketika Anda perlu menerapkan satu fungsi sederhana ke setiap elemen dalam DataFrame.
- Untuk tugas-tugas seperti konversi tipe data, pemformatan string, atau transformasi matematika sederhana di seluruh DataFrame.
- Ingat depresiasinya yang digantikan oleh
DataFrame.map()dalam versi Pandas terbaru.
Pendalaman Performa: Vektorisasi vs. Iterasi
Loop yang "Tersembunyi"
Ini adalah konsep paling krusial untuk dipahami dalam menulis kode Pandas berperforma tinggi. Meskipun apply(), map(), dan applymap() nyaman digunakan, mereka pada dasarnya hanyalah pembungkus mewah di sekitar loop Python. Ketika Anda menggunakan df.apply(..., axis=1), Pandas melakukan iterasi melalui DataFrame Anda baris demi baris, meneruskan setiap baris ke fungsi Anda. Proses ini memiliki overhead yang signifikan dan jauh lebih lambat daripada operasi yang dioptimalkan dalam C atau Cython.
Kekuatan Vektorisasi
Vektorisasi adalah praktik melakukan operasi pada seluruh array (atau Series) sekaligus, daripada pada elemen individual. Pandas dan pustaka dasarnya, NumPy, dirancang khusus untuk menjadi sangat cepat dalam operasi vektorisasi.
Mari kita kembali ke perhitungan 'Total_Cost' kita. Kita menggunakan apply(), tetapi adakah cara vektorisasi?
# Metode 1: Menggunakan apply() (Iterasi)
df['Total_Cost'] = df.apply(lambda row: row['Price_USD'] * row['Quantity'], axis=1)
# Metode 2: Operasi Vektorisasi
df['Total_Cost_Vect'] = df['Price_USD'] * df['Quantity']
# Periksa apakah hasilnya sama
print(df['Total_Cost'].equals(df['Total_Cost_Vect'])) # Output: True
Metode kedua adalah vektorisasi. Ia mengambil seluruh Series 'Price_USD' dan mengalikannya dengan seluruh Series 'Quantity' dalam satu operasi tunggal yang sangat teroptimalkan. Jika Anda mengukur waktu kedua metode ini pada DataFrame besar (jutaan baris), pendekatan vektorisasi tidak hanya akan lebih cepat—ia akan jauh lebih cepat berkali-kali lipat. Kita berbicara tentang detik versus menit, atau menit versus jam.
Kapan `apply()` Tak Terhindarkan?
Jika vektorisasi jauh lebih cepat, mengapa metode lain ini ada? Karena terkadang, logika Anda terlalu kompleks untuk divektorisasi. apply() adalah alat yang diperlukan dan benar ketika:
- Logika Kondisional Kompleks: Logika Anda melibatkan pernyataan `if/elif/else` yang rumit yang bergantung pada beberapa kolom, seperti contoh `assign_shipping_priority` kita. Meskipun sebagian dari ini dapat dicapai dengan `np.select()`, itu bisa menjadi tidak terbaca.
- Fungsi Pustaka Eksternal: Anda perlu menerapkan fungsi dari pustaka eksternal ke data Anda. Misalnya, menerapkan fungsi dari pustaka geospasial untuk menghitung jarak berdasarkan kolom lintang dan bujur, atau fungsi dari pustaka pemrosesan bahasa alami (seperti NLTK) untuk melakukan analisis sentimen pada kolom teks.
- Proses Iteratif: Perhitungan untuk baris tertentu bergantung pada nilai yang dihitung di baris sebelumnya (meskipun ini jarang terjadi dan seringkali merupakan tanda bahwa struktur data yang berbeda diperlukan).
Praktik Terbaik: Vektorisasi Dulu, `apply()` Kemudian
Ini mengarah pada aturan emas kinerja Pandas:
Selalu cari solusi vektorisasi terlebih dahulu. Gunakan `apply()` sebagai cadangan Anda yang kuat dan fleksibel ketika solusi vektorisasi tidak praktis atau tidak mungkin.
Ringkasan dan Poin Kunci: Memilih Alat yang Tepat
Mari kita konsolidasikan pengetahuan kita ke dalam kerangka pengambilan keputusan yang jelas. Ketika dihadapkan pada tugas transformasi kustom, tanyakan pada diri Anda pertanyaan-pertanyaan ini:
Tabel Perbandingan
| Metode | Bekerja Pada | Lingkup Operasi | Fungsi Menerima | Kasus Penggunaan Utama |
|---|---|---|---|---|
| Vektorisasi | Series, DataFrame | Seluruh array sekaligus | T/A (operasi langsung) | Operasi aritmatika, logis. Performa Tertinggi. |
.map() |
Hanya Series | Elemen-demi-elemen | Satu elemen | Mengganti nilai dari sebuah kamus. |
.apply() |
Series, DataFrame | Baris-demi-baris atau Kolom-demi-kolom | Sebuah Series (sebuah baris atau kolom) | Logika kompleks menggunakan beberapa kolom per baris. |
.applymap() |
Hanya DataFrame | Elemen-demi-elemen | Satu elemen | Memformat atau mentransformasi setiap sel dalam DataFrame. |
Diagram Alir Keputusan
- Dapatkah operasi saya diekspresikan menggunakan aritmatika dasar (+, -, *, /) atau operator logis (&, |, ~) pada seluruh kolom?
→ Ya? Gunakan pendekatan vektorisasi. Ini yang tercepat. (mis., `df['col1'] * df['col2']`) - Apakah saya hanya bekerja pada satu kolom, dan tujuan utama saya adalah mengganti nilai berdasarkan kamus?
→ Ya? GunakanSeries.map(). Ini dioptimalkan untuk hal ini. - Apakah saya perlu menerapkan fungsi ke setiap elemen tunggal di seluruh DataFrame saya?
→ Ya? GunakanDataFrame.applymap()(atauDataFrame.map()di Pandas yang lebih baru). - Apakah logika saya kompleks dan memerlukan nilai dari beberapa kolom di setiap baris untuk menghitung satu hasil?
→ Ya? GunakanDataFrame.apply(..., axis=1). Ini adalah alat Anda untuk logika berbasis baris yang kompleks.
Kesimpulan
Menavigasi opsi untuk menerapkan fungsi kustom di Pandas adalah sebuah ritus peralihan bagi setiap praktisi data. Meskipun pada awalnya mungkin tampak dapat dipertukarkan, map(), apply(), dan applymap() adalah alat yang berbeda, masing-masing dengan kekuatan dan kasus penggunaan idealnya sendiri. Dengan memahami perbedaan mereka, Anda dapat menulis kode yang tidak hanya benar tetapi juga lebih mudah dibaca, dipelihara, dan secara signifikan lebih berkinerja.
Ingatlah hierarkinya: utamakan vektorisasi untuk kecepatan mentahnya, gunakan map() untuk substitusi Series yang efisien, pilih applymap() untuk transformasi di seluruh DataFrame, dan manfaatkan kekuatan dan fleksibilitas apply() untuk logika berbasis baris atau kolom yang kompleks yang tidak dapat divektorisasi. Berbekal pengetahuan ini, Anda sekarang lebih siap untuk mengatasi tantangan manipulasi data apa pun yang menghadang, mengubah data mentah menjadi wawasan yang kuat dengan keterampilan dan efisiensi.